3 class ExtensionProcessorTest
extends MediaWikiTestCase
{
7 public function setUp() {
9 $this->dir
= __DIR__
. '/FooBar/extension.json';
13 * 'name' is absolutely required
17 public static $default = [
22 * @covers ExtensionProcessor::extractInfo
24 public function testExtractInfo() {
25 // Test that attributes that begin with @ are ignored
26 $processor = new ExtensionProcessor();
27 $processor->extractInfo( $this->dir
, self
::$default +
[
28 '@metadata' => [ 'foobarbaz' ],
29 'AnAttribute' => [ 'omg' ],
30 'AutoloadClasses' => [ 'FooBar' => 'includes/FooBar.php' ],
33 $extracted = $processor->getExtractedInfo();
34 $attributes = $extracted['attributes'];
35 $this->assertArrayHasKey( 'AnAttribute', $attributes );
36 $this->assertArrayNotHasKey( '@metadata', $attributes );
37 $this->assertArrayNotHasKey( 'AutoloadClasses', $attributes );
40 public static function provideRegisterHooks() {
41 $merge = [ ExtensionRegistry
::MERGE_STRATEGY
=> 'array_merge_recursive' ];
44 // Content in extension.json
45 // Expected value of $wgHooks
53 // No current hooks, adding one for "FooBaz" in string format
56 [ 'Hooks' => [ 'FooBaz' => 'FooBazCallback' ] ] + self
::$default,
57 [ 'FooBaz' => [ 'FooBazCallback' ] ] +
$merge,
59 // Hook for "FooBaz", adding another one
61 [ 'FooBaz' => [ 'PriorCallback' ] ],
62 [ 'Hooks' => [ 'FooBaz' => 'FooBazCallback' ] ] + self
::$default,
63 [ 'FooBaz' => [ 'PriorCallback', 'FooBazCallback' ] ] +
$merge,
65 // No current hooks, adding one for "FooBaz" in verbose array format
68 [ 'Hooks' => [ 'FooBaz' => [ 'FooBazCallback' ] ] ] + self
::$default,
69 [ 'FooBaz' => [ 'FooBazCallback' ] ] +
$merge,
71 // Hook for "BarBaz", adding one for "FooBaz"
73 [ 'BarBaz' => [ 'BarBazCallback' ] ],
74 [ 'Hooks' => [ 'FooBaz' => 'FooBazCallback' ] ] + self
::$default,
76 'BarBaz' => [ 'BarBazCallback' ],
77 'FooBaz' => [ 'FooBazCallback' ],
80 // Callbacks for FooBaz wrapped in an array
83 [ 'Hooks' => [ 'FooBaz' => [ 'Callback1' ] ] ] + self
::$default,
85 'FooBaz' => [ 'Callback1' ],
88 // Multiple callbacks for FooBaz hook
91 [ 'Hooks' => [ 'FooBaz' => [ 'Callback1', 'Callback2' ] ] ] + self
::$default,
93 'FooBaz' => [ 'Callback1', 'Callback2' ],
100 * @covers ExtensionProcessor::extractHooks
101 * @dataProvider provideRegisterHooks
103 public function testRegisterHooks( $pre, $info, $expected ) {
104 $processor = new MockExtensionProcessor( [ 'wgHooks' => $pre ] );
105 $processor->extractInfo( $this->dir
, $info, 1 );
106 $extracted = $processor->getExtractedInfo();
107 $this->assertEquals( $expected, $extracted['globals']['wgHooks'] );
111 * @covers ExtensionProcessor::extractConfig
113 public function testExtractConfig() {
114 $processor = new ExtensionProcessor
;
117 'Bar' => 'somevalue',
129 $processor->extractInfo( $this->dir
, $info, 1 );
130 $processor->extractInfo( $this->dir
, $info2, 1 );
131 $extracted = $processor->getExtractedInfo();
132 $this->assertEquals( 'somevalue', $extracted['globals']['wgBar'] );
133 $this->assertEquals( 10, $extracted['globals']['wgFoo'] );
134 $this->assertArrayNotHasKey( 'wg@IGNORED', $extracted['globals'] );
136 $this->assertEquals( 'somevalue', $extracted['globals']['egBar'] );
139 public static function provideExtractExtensionMessagesFiles() {
140 $dir = __DIR__
. '/FooBar/';
143 [ 'ExtensionMessagesFiles' => [ 'FooBarAlias' => 'FooBar.alias.php' ] ],
144 [ 'wgExtensionMessagesFiles' => [ 'FooBarAlias' => $dir . 'FooBar.alias.php' ] ]
148 'ExtensionMessagesFiles' => [
149 'FooBarAlias' => 'FooBar.alias.php',
150 'FooBarMagic' => 'FooBar.magic.i18n.php',
154 'wgExtensionMessagesFiles' => [
155 'FooBarAlias' => $dir . 'FooBar.alias.php',
156 'FooBarMagic' => $dir . 'FooBar.magic.i18n.php',
164 * @covers ExtensionProcessor::extractExtensionMessagesFiles
165 * @dataProvider provideExtractExtensionMessagesFiles
167 public function testExtractExtensionMessagesFiles( $input, $expected ) {
168 $processor = new ExtensionProcessor();
169 $processor->extractInfo( $this->dir
, $input + self
::$default, 1 );
170 $out = $processor->getExtractedInfo();
171 foreach ( $expected as $key => $value ) {
172 $this->assertEquals( $value, $out['globals'][$key] );
176 public static function provideExtractMessagesDirs() {
177 $dir = __DIR__
. '/FooBar/';
180 [ 'MessagesDirs' => [ 'VisualEditor' => 'i18n' ] ],
181 [ 'wgMessagesDirs' => [ 'VisualEditor' => [ $dir . 'i18n' ] ] ]
184 [ 'MessagesDirs' => [ 'VisualEditor' => [ 'i18n', 'foobar' ] ] ],
185 [ 'wgMessagesDirs' => [ 'VisualEditor' => [ $dir . 'i18n', $dir . 'foobar' ] ] ]
191 * @covers ExtensionProcessor::extractMessagesDirs
192 * @dataProvider provideExtractMessagesDirs
194 public function testExtractMessagesDirs( $input, $expected ) {
195 $processor = new ExtensionProcessor();
196 $processor->extractInfo( $this->dir
, $input + self
::$default, 1 );
197 $out = $processor->getExtractedInfo();
198 foreach ( $expected as $key => $value ) {
199 $this->assertEquals( $value, $out['globals'][$key] );
204 * @covers ExtensionProcessor::extractCredits
206 public function testExtractCredits() {
207 $processor = new ExtensionProcessor();
208 $processor->extractInfo( $this->dir
, self
::$default, 1 );
209 $this->setExpectedException( 'Exception' );
210 $processor->extractInfo( $this->dir
, self
::$default, 1 );
214 * @covers ExtensionProcessor::extractResourceLoaderModules
215 * @dataProvider provideExtractResourceLoaderModules
217 public function testExtractResourceLoaderModules( $input, $expected ) {
218 $processor = new ExtensionProcessor();
219 $processor->extractInfo( $this->dir
, $input + self
::$default, 1 );
220 $out = $processor->getExtractedInfo();
221 foreach ( $expected as $key => $value ) {
222 $this->assertEquals( $value, $out['globals'][$key] );
226 public static function provideExtractResourceLoaderModules() {
227 $dir = __DIR__
. '/FooBar';
229 // Generic module with localBasePath/remoteExtPath specified
233 'ResourceModules' => [
235 'styles' => 'foobar.js',
236 'localBasePath' => '',
237 'remoteExtPath' => 'FooBar',
243 'wgResourceModules' => [
245 'styles' => 'foobar.js',
246 'localBasePath' => $dir,
247 'remoteExtPath' => 'FooBar',
252 // ResourceFileModulePaths specified:
256 'ResourceFileModulePaths' => [
257 'localBasePath' => '',
258 'remoteExtPath' => 'FooBar',
260 'ResourceModules' => [
263 'styles' => 'foo.js',
265 // Different paths set
267 'styles' => 'bar.js',
268 'localBasePath' => 'subdir',
269 'remoteExtPath' => 'FooBar/subdir',
271 // Custom class with no paths set
273 'class' => 'FooBarModule',
274 'extra' => 'argument',
276 // Custom class with a localBasePath
277 'test.class.with.path' => [
278 'class' => 'FooBarPathModule',
279 'extra' => 'argument',
280 'localBasePath' => '',
286 'wgResourceModules' => [
288 'styles' => 'foo.js',
289 'localBasePath' => $dir,
290 'remoteExtPath' => 'FooBar',
293 'styles' => 'bar.js',
294 'localBasePath' => "$dir/subdir",
295 'remoteExtPath' => 'FooBar/subdir',
298 'class' => 'FooBarModule',
299 'extra' => 'argument',
300 'localBasePath' => $dir,
301 'remoteExtPath' => 'FooBar',
303 'test.class.with.path' => [
304 'class' => 'FooBarPathModule',
305 'extra' => 'argument',
306 'localBasePath' => $dir,
307 'remoteExtPath' => 'FooBar',
312 // ResourceModuleSkinStyles with file module paths
316 'ResourceFileModulePaths' => [
317 'localBasePath' => '',
318 'remoteSkinPath' => 'FooBar',
320 'ResourceModuleSkinStyles' => [
322 'test.foo' => 'foo.css',
328 'wgResourceModuleSkinStyles' => [
330 'test.foo' => 'foo.css',
331 'localBasePath' => $dir,
332 'remoteSkinPath' => 'FooBar',
337 // ResourceModuleSkinStyles with file module paths and an override
341 'ResourceFileModulePaths' => [
342 'localBasePath' => '',
343 'remoteSkinPath' => 'FooBar',
345 'ResourceModuleSkinStyles' => [
347 'test.foo' => 'foo.css',
348 'remoteSkinPath' => 'BarFoo'
354 'wgResourceModuleSkinStyles' => [
356 'test.foo' => 'foo.css',
357 'localBasePath' => $dir,
358 'remoteSkinPath' => 'BarFoo',
366 public static function provideSetToGlobal() {
369 [ 'wgAPIModules', 'wgAvailableRights' ],
372 'APIModules' => [ 'foobar' => 'ApiFooBar' ],
373 'AvailableRights' => [ 'foobar', 'unfoobar' ],
376 'wgAPIModules' => [ 'foobar' => 'ApiFooBar' ],
377 'wgAvailableRights' => [ 'foobar', 'unfoobar' ],
381 [ 'wgAPIModules', 'wgAvailableRights' ],
383 'wgAPIModules' => [ 'barbaz' => 'ApiBarBaz' ],
384 'wgAvailableRights' => [ 'barbaz' ]
387 'APIModules' => [ 'foobar' => 'ApiFooBar' ],
388 'AvailableRights' => [ 'foobar', 'unfoobar' ],
391 'wgAPIModules' => [ 'barbaz' => 'ApiBarBaz', 'foobar' => 'ApiFooBar' ],
392 'wgAvailableRights' => [ 'barbaz', 'foobar', 'unfoobar' ],
396 [ 'wgGroupPermissions' ],
398 'wgGroupPermissions' => [
399 'sysop' => [ 'delete' ]
403 'GroupPermissions' => [
404 'sysop' => [ 'undelete' ],
409 'wgGroupPermissions' => [
410 'sysop' => [ 'delete', 'undelete' ],
420 * Allow overriding the default value of $this->globals
421 * so we can test merging
423 class MockExtensionProcessor
extends ExtensionProcessor
{
424 public function __construct( $globals = [] ) {
425 $this->globals
= $globals +
$this->globals
;